package com.apestech.framework.json;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;


public class JSONObject extends JSON implements Map<String, Object>, Cloneable, Serializable, InvocationHandler {

	protected static final long         serialVersionUID         = 1L;
	protected static final int          DEFAULT_INITIAL_CAPACITY = 16;

    private final Map<String, Object> map;
    private String firstKey;

    public JSONObject(Map<String, Object> map){
        this.map=new HashMap<String, Object>();
        if(map!=null) {
            this.map.putAll(map);
        }
    }
    
    public JSONObject(String jsonStr) throws Exception{
        this.map = this.toJSON(jsonStr);
    }
    
    public JSONObject(){
        this(DEFAULT_INITIAL_CAPACITY, false);
    }

    public JSONObject(boolean ordered){
        this(DEFAULT_INITIAL_CAPACITY, ordered);
    }

    public JSONObject(int initialCapacity){
        this(initialCapacity, false);
    }

    public JSONObject(int initialCapacity, boolean ordered){
        if (ordered) {
            map = new LinkedHashMap<String, Object>(initialCapacity);
        } else {
            map = new HashMap<String, Object>(initialCapacity);
        }
    }

    public String getFirstKey() {
        return firstKey;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public int size() {
        return map.size();
    }

    public boolean isEmpty() {
        return map.isEmpty();
    }

    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    public Object get(Object key) {
        Object val = map.get(key);
        if (val == null && key instanceof Number) {
            val = map.get(key.toString());
        }
        return val;
    }

    public JSONObject getJSONObject(String key) throws Exception {
    	Object value = map.get(key);
        if (value instanceof JSONObject) {
            return JSON.toJSON(value);
        }
        if (value instanceof String) {
            return toJSON((String) value);
        }
        return toJSON(value);
    }

    public JSONArray getJSONArray(String key) throws Exception {
    	Object value = map.get(key);
        if (value instanceof JSONArray) {
            return (JSONArray) value;
        }
        if (value instanceof String) {
            return toJSONArray((String) value);
        }
        return (JSONArray) value;
    }

    public <T> T getObject(String key, Class<T> clazz) throws Exception {
        Object obj = map.get(key);
        return JSON.parse(obj, clazz);
    }    

    public Boolean getBoolean(String key) throws Exception {
        Object value = get(key);
        if (value == null) {
            return null;
        }
        return castToBoolean(value);
    }

    public byte[] getBytes(String key) throws Exception {
        Object value = get(key);
        if (value == null) {
            return null;
        }
        return castToBytes(value);
    }

    public boolean getBooleanValue(String key) throws Exception {
        Object value = get(key);
        Boolean booleanVal = castToBoolean(value);
        if (booleanVal == null) {
            return false;
        }
        return booleanVal.booleanValue();
    }

    public Byte getByte(String key) throws Exception {
        Object value = get(key);
        return castToByte(value);
    }

    public byte getByteValue(String key) throws Exception {
        Object value = get(key);
        Byte byteVal = castToByte(value);
        if (byteVal == null) {
            return 0;
        }
        return byteVal.byteValue();
    }

    public Short getShort(String key) throws Exception {
        Object value = get(key);
        return castToShort(value);
    }

    public short getShortValue(String key) throws Exception {
        Object value = get(key);
        Short shortVal = castToShort(value);
        if (shortVal == null) {
            return 0;
        }
        return shortVal.shortValue();
    }

    public Integer getInteger(String key) throws Exception {
        Object value = get(key);
        return castToInt(value);
    }

    public int getIntValue(String key) throws Exception {
        Object value = get(key);
        Integer intVal = castToInt(value);
        if (intVal == null) {
            return 0;
        }
        return intVal.intValue();
    }

    public Long getLong(String key) throws Exception {
        Object value = get(key);
        return castToLong(value);
    }

    public long getLongValue(String key) throws Exception {
        Object value = get(key);
        Long longVal = castToLong(value);
        if (longVal == null) {
            return 0L;
        }
        return longVal.longValue();
    }

    public Float getFloat(String key) throws Exception {
        Object value = get(key);
        return castToFloat(value);
    }

    public float getFloatValue(String key) throws Exception {
        Object value = get(key);

        Float floatValue = castToFloat(value);
        if (floatValue == null) {
            return 0F;
        }
        return floatValue.floatValue();
    }

    public Double getDouble(String key) throws Exception {
        Object value = get(key);
        return castToDouble(value);
    }

    public double getDoubleValue(String key) throws Exception {
        Object value = get(key);
        Double doubleValue = castToDouble(value);
        if (doubleValue == null) {
            return 0D;
        }
        return doubleValue.doubleValue();
    }

    public BigDecimal getBigDecimal(String key) {
        Object value = get(key);
        return castToBigDecimal(value);
    }

    public BigInteger getBigInteger(String key) {
        Object value = get(key);
        return castToBigInteger(value);
    }

    public String getString(String key) {
        Object value = get(key);
        if (value == null) {
            return null;
        }
        return value.toString();
    }

    public Date getDate(String key) throws Exception {
        Object value = get(key);
        return castToDate(value);
    }

    public java.sql.Date getSqlDate(String key) throws Exception {
        Object value = get(key);
        return castToSqlDate(value);
    }

    public java.sql.Timestamp getTimestamp(String key) throws Exception {
        Object value = get(key);
        return castToTimestamp(value);
    }
    
    public Object put(String key, Object value) {
        if(firstKey==null) firstKey=key;
        return map.put(key, value);
    }
    
    public JSONObject fluentPut(String key, Object value) {
        map.put(key, value);
        return this;
    }

    public void putAll(Map<? extends String, ? extends Object> m) {
        if(m==null) return;
        map.putAll(m);
    }

    public void putAllDetail(JSONObject m) {
        if(m==null) return;
        ArrayList<String> klist=m.getKeylist();
        int len=klist.size();
        for(int i=0; i<len; i++) {
            String k = klist.get(i);
            Object obj=m.get(k);
            if(obj instanceof JSONObject){
                map.put(k, getDetailMap((JSONObject)map.get(k), (JSONObject)m.get(k)));
            } else map.put(k, obj);
        }

    }

    private JSONObject getDetailMap(JSONObject in, JSONObject out){
        if(in==null) return out;
        JSONObject m=new JSONObject();
        m.putAll(in);
        ArrayList<String> klist=out.getKeylist();
        int len=klist.size();
        for(int i=0; i<len; i++){
            String k = klist.get(i);
            Object obj=out.get(k);
            if(obj instanceof JSONObject){
                m.put(k, getDetailMap((JSONObject)in.get(k), (JSONObject)obj));
            } else m.put(k, obj);
        }
        return m;
    }

    public JSONObject fluentPutAll(Map<? extends String, ? extends Object> m) {
        map.putAll(m);
        return this;
    }

    public void clear() {
        map.clear();
    }

    public JSONObject fluentClear() {
        map.clear();
        return this;
    }

    public Object remove(Object key) {
        return map.remove(key);
    }

    public JSONObject fluentRemove(Object key) {
        map.remove(key);
        return this;
    }

    public Set<String> keySet() {
        return map.keySet();
    }

    public Collection<Object> values() {
        return map.values();
    }

    public Set<Entry<String, Object>> entrySet() {
        return map.entrySet();
    }

    @Override
    public Object clone() {
        return new JSONObject(map instanceof LinkedHashMap //
                              ? new LinkedHashMap<String, Object>(map) //
                              : new HashMap<String, Object>(map)   );
    }

    public boolean equals(Object obj) {
        return this.map.equals(obj);
    }

    public int hashCode() {
        return this.map.hashCode();
    }

    public Map<String, Object> getInnerMap() {
        return this.map;
    }
    
    public String toString() {
    	try {
			return toJSONString(this.map);
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return "";
    }
	
    public ArrayList<String> getKeylist(){
    	ArrayList<String> keylist=new ArrayList<String>();
    	if(map!=null && map.size()>0){
    		String[] klist=getKeyStr().split(";");
    		int len=klist.length;
    		for(int i=0; i<len; i++){
    			if(!"".equals(klist[i])){
    				keylist.add(klist[i]);
    			}
    		}
    	}
    	return keylist;
    }
    
    public String getKeyStr(){
    	if(map!=null && map.size()>0){
    	    String key="";
    		Iterator<String> keyValue = map.keySet().iterator();
    		while(keyValue.hasNext()){
    		    if(!"".equals(key)){
    		        key=key+";";
                }
    		    key=key+keyValue.next();
            }
    		return key;
    	} else {
    		return "";
    	}
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return map;
    }
}
